module es {

    export class SpineFactory {

        /** 版本号 */
        protected static version: Laya.SpineVersion;
        /** 缓存池 */
        protected static cachePool: { [url: string]: Laya.SpineTemplet } = {};
        /** 加载完成事件 */
        protected static loadedCallback: Map<Laya.SpineTemplet, Laya.Handler[]> = new Map<Laya.SpineTemplet, Laya.Handler[]>();

        /**
         * 初始化
         * @param ver Spine版本号
         */
        public static init(ver: Laya.SpineVersion) {
            SpineFactory.version = ver;
        }

        /**
         * 提前预加载
         * @param url .skel文件url
         * @param cb 加载完成回调
         */
        public static load(url: string, cb: Laya.Handler) {
            SpineFactory.loads([url], cb);
        }

        /**
         * 提前预加载
         * @param urls  Array<.skel>
         * @param cb 加载完成回调
         */
        public static loads(urls: string[], cb: Laya.Handler) {
            for (let i = 0; i < urls.length; i++) {
                let url = urls[i];
                if (!SpineFactory.cachePool[url]) {
                    let st = SpineFactory.cachePool[url] = new Laya.SpineTemplet(SpineFactory.version);
                    (i == urls.length - 1) && SpineFactory.bindLoadedCallback(st, cb);
                    st.on(Laya.Event.COMPLETE, SpineFactory, SpineFactory.loadComplete);
                    st.on(Laya.Event.ERROR, SpineFactory, SpineFactory.loadFail);
                    st.loadAni(url);
                }
                else if (!SpineFactory.cachePool[url]['__loaded']) {
                    SpineFactory.bindLoadedCallback(SpineFactory.cachePool[url], cb);
                }
                else {
                    cb && cb.runWith(SpineFactory.cachePool[url]);
                }
            }
        }

        /** 添加 加载结束回调 */
        private static bindLoadedCallback(st: Laya.SpineTemplet, cb: Laya.Handler) {
            let cbs = SpineFactory.loadedCallback.get(st);
            if (!cbs) {
                cbs = [];
            }
            cbs.push(cb);
            SpineFactory.loadedCallback.set(st, cbs);
        }

        /** 加载完成 */
        protected static loadComplete(obj) {
            obj['__loaded'] = true;
            let cbs = SpineFactory.loadedCallback.get(obj);
            cbs && [].forEach.call(cbs, cb => {
                cb && cb.runWith(obj);
            });
        }

        /** 加载失败 */
        protected static loadFail(e) {
            console.error(e);
        }

        /**
         * 生成新的龙骨
         * @param in_url 龙骨url
         * @param out_res 加载完成后的回调
         */
        public static spawn(in_url: string, out_res: (spine: Laya.SpineSkeleton) => void): void {
            if (SpineFactory.cachePool[in_url] && SpineFactory.cachePool[in_url]['__loaded']) {
                out_res(SpineFactory.cachePool[in_url].buildArmature());
            }
            else {
                SpineFactory.load(in_url, Laya.Handler.create(this, (st: Laya.SpineTemplet) => {
                    out_res(st.buildArmature());
                }));
            }
        }

        /**
         * 添加一个动画播放停止的事件监听 
         * @param spine 
         * @param handler 
         * @param once 
         */
        public static AddCompleteHandle(spine: Laya.SpineSkeleton, caller: any, func: Function, once = true) {

            if (once) {

                spine.once(Laya.Event.STOPPED, caller, func);
            } else {

                spine.on(Laya.Event.STOPPED, caller, func);
            }
        }

        /**
         * 移除Spine动画 停止事件监听
         * 
         * @param spine 指定的Laya.SpineSkeleton实例
         * @param caller 如果 caller 和 func 都存在 则指定删除，如果仅有caller，则移除spine上所有关于caller的监听
         * @param func 仅配合caller使用 指定删除某对象某一方法
         */
        public static RemoveCompleteHandle(spine: Laya.SpineSkeleton, caller?: any, func?: Function) {
            if (caller && func) {
                spine.off(Laya.Event.STOPPED, caller, func);
            }
            else if (caller) {
                spine.offAllCaller(caller);
            }
            else {
                spine.offAll(Laya.Event.STOPPED);
            }
        }
    }
}